package info.batcloud.pdd.sdk;

import com.alibaba.fastjson.JSON;
import info.batcloud.pdd.sdk.annotation.Param;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.springframework.util.DigestUtils;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.*;

public class DefaultPddClient implements PddClient {

    private String clientId;

    private String clientSecret;

    private String serverUrl;

    public DefaultPddClient(String serverUrl, String clientId, String clientSecret) {
        this.clientId = clientId;
        this.clientSecret = clientSecret;
        this.serverUrl = serverUrl;
    }

    public String getClientId() {
        return clientId;
    }

    public void setClientId(String clientId) {
        this.clientId = clientId;
    }

    public String getClientSecret() {
        return clientSecret;
    }

    public void setClientSecret(String clientSecret) {
        this.clientSecret = clientSecret;
    }

    @Override
    public <T extends PddResponse> T execute(PddRequest<T> request) {
        return execute(request, null);
    }

    @Override
    public <T extends PddResponse> T execute(PddRequest<T> request, String accessToken) {

        List<String> list = new ArrayList<>();
        Map<String, String> params = new HashMap<>();
        params.put("type", request.getType());
        params.put("client_id", clientId);
        params.put("timestamp", System.currentTimeMillis() + "");
        if (accessToken != null) {
            params.put("access_token", accessToken);
        }
        Class requestClazz = request.getClass();
        for (Field field : requestClazz.getDeclaredFields()) {
            if (field.isAnnotationPresent(Param.class)) {
                Param param = field.getAnnotation(Param.class);
                field.setAccessible(true);
                try {
                    Object val = field.get(request);
                    if (val == null) {
                        continue;
                    }
                    String strVal;
                    if (val instanceof List) {
                        strVal = JSON.toJSONString(val);
                    } else {
                        strVal = val.toString();
                    }
                    params.put(StringUtils.defaultIfBlank(param.value(), field.getName()), strVal);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        params.forEach((k, v) -> list.add(k + v));
        Collections.sort(list);
        String signStr = this.clientSecret + String.join("", list) + this.clientSecret;
        try {
            String sign = DigestUtils.md5DigestAsHex(signStr.getBytes("utf8"));
            params.put("sign", sign.toUpperCase());
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        list.clear();
        params.forEach((k, v) -> {
            try {
                list.add(k + "=" + URLEncoder.encode(v, "utf8"));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        });
        String api = serverUrl + "?" + String.join("&", list);
        CloseableHttpClient client = HttpClientBuilder.create().build();
        HttpPost httpPost = new HttpPost(api);
        httpPost.setHeader("Content-Type", "application/json");
        try {
            HttpResponse res = client.execute(httpPost);
            String result = EntityUtils.toString(res.getEntity(), "utf8");
            client.close();
            T response = JSON.parseObject(result, request.getResponseClass());
            return response;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

}
